package com.t3go.t3_map.service;

import static com.t3map.api.map.utils.T3MapUtils.isPointValid;

import android.app.ActivityManager;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.alibaba.android.arouter.facade.annotation.Route;
import com.shareware.common.listener.Callback;
import com.shareware.common.utils.AppExtKt;
import com.shareware.common.utils.LogExtKt;
import com.t3go.passenger.base.common.Const;
import com.t3go.passenger.base.config.MapConstants;
import com.t3go.passenger.base.entity.AddressEntity;
import com.t3go.passenger.base.utils.JsonUtils;
import com.t3go.passenger.base.utils.SP;
import com.t3go.passenger.router.ILocationRouterService;
import com.t3map.api.T3MapsInitializer;
import com.t3map.api.entity.PoiSearchOption;
import com.t3map.api.entity.PoiSource;
import com.t3map.api.location.IT3LocationService;
import com.t3map.api.location.T3LocationManagerService;
import com.t3map.api.location.entity.T3MapLocation;
import com.t3map.api.map.entity.LocationEntity;
import com.t3map.api.map.model.T3LatLng;
import com.t3map.api.map.utils.T3MapUtils;
import com.t3map.api.service.IT3SearchService;
import com.t3map.api.service.T3RouteSearchManager;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 位置相关服务
 *
 * @author yasin
 */
@Route(path = LocationRouterServiceImpl.LOCATION_SERVICE)
public class LocationRouterServiceImpl implements ILocationRouterService {

    public static final String LOCATION_SERVICE = "/location/router_service";

    public static final String REGEO_TAG = "regeo_tag";
    private Const.LocationInfo curConstLocation;

    private static T3MapLocation mCurLocation = null;
    private LocationCallback mCallBack;
    private Context mContext;
    private SP mSP;
    private Callback<AddressEntity> sSeriesLocationCallback;

    @Override
    public void init(Context context) {
        mContext = context;
        mCallBack = new LocationCallback();
        mSP = new SP();
    }

    @Override
    public void setSeriesLocationCallback(Callback<AddressEntity> seriesLocationCallback) {
        sSeriesLocationCallback = seriesLocationCallback;
    }

    @Override
    public void initSeriesLocation() {
        try {
            IT3LocationService locationService = T3LocationManagerService.getInstance(T3MapsInitializer.getMapType());
            locationService.initSeriesLocation(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void startSeriesLocation() {
        try {
            IT3LocationService locationService = T3LocationManagerService.getInstance(T3MapsInitializer.getMapType());
            locationService.startSeriesLocation(mCallBack);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Override
    public void stopSeriesLocation() {
        if (isServiceRunning(mContext)) {
            LogExtKt.log(LOCATION_TAG, "服务正在运行中，不可关闭定位回调: ");
            return;
        }
        // APP 在前台 不关闭
        if (!AppExtKt.getAppBackground()) {
            LogExtKt.log(LOCATION_TAG, "APP在前台，不可关闭定位回调: ");
            return;
        }
        try {
            IT3LocationService locationService = T3LocationManagerService.getInstance(T3MapsInitializer.getMapType());
            locationService.stopSeriesLocation(mCallBack);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public T3MapLocation getLastKnownLocation() {
        try {
            IT3LocationService locationService = T3LocationManagerService.getInstance(T3MapsInitializer.getMapType());
            return locationService.getLastKnownLocation();
        } catch (Exception e) {
            LogExtKt.log(ILocationRouterService.LOCATION_TAG, "getLastKnownLocation Exception.");
        }
        return null;
    }

    @Override
    public T3MapLocation getCurLocation() {
        return mCurLocation;
    }

    @Override
    public void activeGetCurrentLocation(Callback<AddressEntity> callback) {
        try {
            IT3LocationService locationService = T3LocationManagerService.getInstance(T3MapsInitializer.getMapType());
            locationService.getCurrentLocation(locationEntity -> {
                if (locationEntity == null) {
                    LogExtKt.log(ILocationRouterService.LOCATION_TAG, "activeGetCurrentLocation error");
                    if (callback != null) {
                        callback.call(null);
                    }
                    return;
                }
                callLocationResult(locationEntity, callback);
            });
        } catch (Exception e) {
            LogExtKt.logStackTrace(ILocationRouterService.LOCATION_TAG, e);
        }
    }

    @Override
    public void activeGetFirstLocation(Callback<AddressEntity> callback) {
        if (T3MapsInitializer.getMapType() != T3MapsInitializer.MAP_TYPE_TENCENT) {
            activeGetCurrentLocation(callback);
            Log.d("TAG", "start1: +locationToMoveMap->home getMyLocation");

        } else {
            try {
                IT3LocationService locationService = T3LocationManagerService.getInstance(T3MapsInitializer.getMapType());
                locationService.initSeriesLocation(1000);
                Log.d("TAG", "start2: +locationToMoveMap->home getMyLocation");

                locationService.startSeriesLocation(new Callback<T3MapLocation>() {
                    int maxCount = 3;

                    @Override
                    public void call(@androidx.annotation.Nullable T3MapLocation locationEntity) {
                        maxCount--;
                        Log.d("TAG", "call: +locationToMoveMap->home getMyLocation");
                        if (locationEntity == null) {
                            // 最后一条数据
                            if (maxCount <= 0) {
                                LogExtKt.log(ILocationRouterService.LOCATION_TAG, "activeGetCurrentLocation error");
                                if (callback != null) {
                                    callback.call(null);
                                }
                                locationService.stopSeriesLocation(this);
                            }
                        } else {
                            // GPS/北斗有结果 或者 定位精度在0-100 路试发现精度高可能是缓存，产生误差
                            if (locationEntity.getLocationType() == T3MapLocation.PROVIDER_GPS || maxCount <= 0) {
                                callLocationResult(locationEntity, callback);
                                locationService.stopSeriesLocation(this);
                            }
                        }
                    }
                });
            } catch (Exception e) {
                LogExtKt.logStackTrace(ILocationRouterService.LOCATION_TAG, e);
            }
        }
    }

    private void callLocationResult(@androidx.annotation.Nullable T3MapLocation locationEntity, Callback<AddressEntity> callback) {
        if (locationEntity == null) {
            return;
        }
        if (!TextUtils.isEmpty(locationEntity.getAdCode())) {
            saveUserCurrentLocation(locationEntity, null);
            AddressEntity addressEntity = new AddressEntity(locationEntity);
            //返回有完整信息
            if (callback != null) {
                callback.call(addressEntity);
            }
        } else {

            // 对没有地址信息的位置 进行逆地址解析 同时进行缓存 距离在1km以内 认为是同一个地区 不再次逆地址
            T3LatLng lastLatLng = mSP.getObject(MapConstants.KEY_LAST_KNOW_LOCATION, T3LatLng.class);
            T3LatLng currentLatLng = locationEntity.getT3LatLng();
            //没有缓存 或者两点位置超过1km 逆地址
            if (lastLatLng == null || calculateLineDistance(currentLatLng, lastLatLng) > 1000) {
                LogExtKt.log(LOCATION_TAG, "进行逆地址解析start MyLocationStyle.latlng = " + JsonUtils.toJson(locationEntity));
                // 高德返回locationType=1即GPS定位结果的时候，只有经纬度和高度等信息，没有adCode等其他信息，此时逆地理一把
                reGeoCodeQuery(locationEntity.getLat(), locationEntity.getLng(), addressEntity -> {
                    if (addressEntity == null) {
                        //返回的数据是有经纬度等，无adCode
                        addressEntity = new AddressEntity(locationEntity);
                        saveUserCurrentLocation(locationEntity, null);
                    } else {
                        saveUserCurrentLocation(locationEntity, addressEntity);
                    }
                    if (callback != null) {
                        callback.call(addressEntity);
                    }
                });
            } else {
                // 缓存了 直接使用
                String adCode = mSP.getString(MapConstants.KEY_AD_CODE, "");
                String cityCode = mSP.getString(MapConstants.KEY_CITY_CODE, "");
                String city = mSP.getString(MapConstants.KEY_CITY_NAME, "");
                String addressTitle = mSP.getString(MapConstants.KEY_ADDRESS_TITLE, "");
                String address = mSP.getString(MapConstants.KEY_ADDRESS, "");
                AddressEntity addressEntity = new AddressEntity(locationEntity);
                if (!TextUtils.isEmpty(adCode)) {
                    addressEntity.setAdCode(adCode);
                }
                if (!TextUtils.isEmpty(cityCode)) {
                    addressEntity.setCityCode(cityCode);
                }
                if (!TextUtils.isEmpty(city)) {
                    addressEntity.setCity(city);
                }
                if (!TextUtils.isEmpty(addressTitle)) {
                    addressEntity.setAddressTitle(addressTitle);
                }
                if (!TextUtils.isEmpty(address)) {
                    addressEntity.setAddress(address);
                }

                LogExtKt.log(LOCATION_TAG, "缓存了 直接使用 MyLocationStyle.cityCode = " + cityCode + ", adCode: " + adCode + ", city: " + city + ",addressTitle=" + addressTitle);
                saveUserCurrentLocation(locationEntity, addressEntity);
                if (callback != null) {
                    callback.call(addressEntity);
                }
            }
        }
    }

    public static float calculateLineDistance(T3LatLng origin, T3LatLng dest) {
        if (isPointValid(origin) && isPointValid(dest)) {
            return T3MapUtils.calculateLineDistance(origin, dest);
        }
        return -1;
    }

    private void saveUserCurrentLocation(T3MapLocation locationEntity, AddressEntity addressEntity) {
        if (locationEntity == null) {
            LogExtKt.log(ILocationRouterService.LOCATION_TAG, "saveUserCurrentLocation1 return, locationEntity is null");
            return;
        }
        String adCode = locationEntity.getAdCode();
        String cityCode = locationEntity.getCityCode();
        String city = locationEntity.getCity();
        String addressTitle = locationEntity.getPoiName();//短地址
        String address = locationEntity.getAddressTitle();//详细地址
        if (addressEntity != null) {
            if (!TextUtils.isEmpty(addressEntity.getAdCode())) {
                adCode = addressEntity.getAdCode();
                locationEntity.setAdCode(adCode);
            }
            if (!TextUtils.isEmpty(addressEntity.getCityCode())) {
                cityCode = addressEntity.getCityCode();
                locationEntity.setCityCode(cityCode);
            }
            if (!TextUtils.isEmpty(addressEntity.getCity())) {
                city = addressEntity.getCity();
                locationEntity.setCity(city);
            }
            if (!TextUtils.isEmpty(addressEntity.getAddress())) {
                address = addressEntity.getAddress();
                locationEntity.setAddress(address);
            }
            if (!TextUtils.isEmpty(addressEntity.getAddressTitle())) {
                addressTitle = addressEntity.getAddressTitle();
                locationEntity.setAddressTitle(addressTitle);
            }

        }

        if (TextUtils.isEmpty(adCode)) {
            LogExtKt.log(ILocationRouterService.LOCATION_TAG, "saveUserCurrentLocation2 return, adCode is null");
            return;
        }
        LogExtKt.log(ILocationRouterService.LOCATION_TAG, "saveUserCurrentLocation:" + adCode);
        // saveLocationToMemory
        mCurLocation = locationEntity;
        //saveLocationToCache
        T3LatLng t3LatLng = new T3LatLng(locationEntity.getLat(), locationEntity.getLng());
        SP sp = new SP();
        sp.putString(MapConstants.KEY_AD_CODE, adCode);
        sp.putString(MapConstants.KEY_CITY_CODE, cityCode);
        sp.putString(MapConstants.KEY_CITY_NAME, city);
        sp.putString(MapConstants.KEY_ADDRESS_TITLE, addressTitle);
        sp.putString(MapConstants.KEY_ADDRESS, address);
        sp.putObject(MapConstants.KEY_LAST_KNOW_LOCATION, t3LatLng);
        //saveLocationToConst
        Const.LocationInfo constLocation = new Const.LocationInfo(locationEntity.getLat(), locationEntity.getLng(), cityCode, adCode, city);
        constLocation.addressTitle = addressTitle;
        constLocation.address = address;
        constLocation.accuracy = locationEntity.getAccuracy();
        constLocation.locationType = locationEntity.getLocationType();
        constLocation.userLocationSource = "AMap-activeGetCurrentLocation";
        //MapReportUtils.reportStartLocation(constLocation, curConstLocation);
        curConstLocation = constLocation;
        Const.setUserCurrentLocation(constLocation);
    }

    @Override
    public void reGeoCodeQuery(double lat, double lng, Callback<AddressEntity> callback) {
        LogExtKt.log(REGEO_TAG, "use sdk regeo");
        regeoCodeFromSDK(lat, lng, callback);
    }

    private void regeoCodeFromSDK(double lat, double lng, Callback<AddressEntity> callback) {
        LogExtKt.log(REGEO_TAG, "regeoCodeFromSDK");
        IT3SearchService locationRouterService = T3RouteSearchManager.getInstance(T3MapsInitializer.getMapType());
        locationRouterService.reGeoCodeQuery(lat, lng, locationEntity -> {
            if (callback != null) {
                if (locationEntity == null) {
                    callback.call(null);
                    return;
                }
                AddressEntity addressEntity = new AddressEntity(locationEntity);
                addressEntity.setPoiSource(T3MapsInitializer.getMapType() == T3MapsInitializer.MAP_TYPE_AMAP ?
                        PoiSource.RE_GEO_GD.getMode() : PoiSource.RE_GEO_TX.getMode());
                //MapFoundation.formatAddressInfo(addressEntity);
                callback.call(addressEntity);
            }
        });
    }

    @Override
    public void poiSearchByKeyWord(String keyWord, T3LatLng latLng, int addressType, String poiType, String cityName, AtomicBoolean searchDown, Callback<List<AddressEntity>> callback) {
        IT3SearchService locationRouterService = T3RouteSearchManager.getInstance(T3MapsInitializer.getMapType());
        Const.LocationInfo currentLocation = Const.getUserCurrentLocation();
        PoiSearchOption option = new PoiSearchOption(keyWord, cityName).addressType(addressType).latLng(new T3LatLng(currentLocation.lat, currentLocation.lng)).searchDown(searchDown);
        locationRouterService.poiSearchByKeyWord(option, locationEntityList -> {
            if (callback != null) {
                List<AddressEntity> addressEntityList = new ArrayList<>();
                if (locationEntityList != null) {
                    for (LocationEntity location : locationEntityList) {
                        AddressEntity entity = new AddressEntity(location);
                        entity.setPoiSource(T3MapsInitializer.getMapType() == T3MapsInitializer.MAP_TYPE_AMAP ?
                                PoiSource.SEARCH_SDK_GD.getMode() : PoiSource.SEARCH_SDK_TX.getMode());
                        addressEntityList.add(entity);
                    }
                }
                callback.call(addressEntityList);
            }
        });
    }

    @Override
    public void poiSearch(String keyWord, String poiType, T3LatLng latLng, int radius, Callback<List<AddressEntity>> callback) {
        IT3SearchService locationRouterService = T3RouteSearchManager.getInstance(T3MapsInitializer.getMapType());
        locationRouterService.poiSearch(keyWord, poiType, latLng, radius, locationEntityList -> {
            if (callback != null) {
                List<AddressEntity> addressEntityList = new ArrayList<>();
                if (locationEntityList != null) {
                    for (LocationEntity location : locationEntityList) {
                        AddressEntity entity = new AddressEntity(location);
                        entity.setPoiSource(T3MapsInitializer.getMapType() == T3MapsInitializer.MAP_TYPE_AMAP ?
                                PoiSource.SEARCH_SDK_GD.getMode() : PoiSource.SEARCH_SDK_TX.getMode());
                        addressEntityList.add(entity);
                    }
                }
                callback.call(addressEntityList);
            }
        });
    }

    @Override
    public void geocodeQuery(String cityName, Callback<AddressEntity> callback) {
        IT3SearchService locationRouterService = T3RouteSearchManager.getInstance(T3MapsInitializer.getMapType());

        locationRouterService.geoCodeQuery("市政府", cityName, locationEntity -> {
            if (callback != null) {
                if (locationEntity == null) {
                    callback.call(null);
                } else {
                    callback.call(new AddressEntity(locationEntity));
                }
            }
        });
    }

    public class LocationCallback implements Callback<T3MapLocation> {
        @Override
        public void call(T3MapLocation locationEntity) {
            LogExtKt.log(LOCATION_TAG, "全局定位回调：" + JsonUtils.toJson(locationEntity));

            if (locationEntity == null) {
                LogExtKt.log(LOCATION_TAG, "locationEntity is null!");
                return;
            }
            if (!locationEntity.getT3LatLng().isValid()) {
                LogExtKt.log(LOCATION_TAG, "locationEntity is inValid!");
                return;
            }
            mCurLocation = locationEntity;
            //  saveUserCurrentLocation(locationEntity, null);
            Const.setCurrentLatLng(locationEntity.getT3LatLng());
            callLocationResult(locationEntity, sSeriesLocationCallback);
        }
    }


    private static boolean isServiceRunning(Context context) {
        ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (service.service.getClassName().equals("com.t3go.passenger.business.trace.LocationAndReportService")) {
                return true;
            }
        }
        return false;
    }


}
